package com.shloma.bbts.tcp.server;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

/**
 * 接收银行请求服务端
 * <p>
 *
 * @author xuefeng.shi
 * @date 2018/12/09
 * @version v1.0.0
 */
@Service
public class ReceiveBankRequestServer implements Runnable {

	private static Logger logger = LoggerFactory.getLogger(ReceiveBankRequestServer.class);

	private Selector selector;
	private ServerSocketChannel serverSocketChannel;

	private int port;
	private String charset;
	private boolean running = false;

	public void startServer(int port, String charset) {
		logger.info(">>>>> 启动接收银行请求服务开始 port:{} pkgCharset:{}", port, charset);
		this.port = port;
		this.charset = charset;

		running = true;
		new Thread(this).start();
		logger.info(">>>>> 启动接收银行请求服务结束");
	}

	public void stopServer() {
		logger.info(">>>>> 停止接收银行请求服务开始");
		running = false;
		try {
			if (serverSocketChannel != null) {
				serverSocketChannel.close();
			}
			if (selector != null) {
				selector.close();
			}
		} catch (IOException e) {
			logger.error("###### 停止监听银行请求服务异常", e);
		}
		logger.info(">>>>> 停止接收银行请求服务结束");
	}

	@SuppressWarnings("rawtypes")
	@Override
	public void run() {
		try {
			selector = Selector.open();
			serverSocketChannel = ServerSocketChannel.open();
			serverSocketChannel.socket().bind(new InetSocketAddress(this.port));
			serverSocketChannel.configureBlocking(false);
			SelectionKey selectionKey0 = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
			selectionKey0.attach(new Acceptor(this.charset));

			while (running) {
				selector.select(5000 * 1001);
				if (selector.isOpen()) {
					Set selected = selector.selectedKeys();
					Iterator it = selected.iterator();
					while (it.hasNext()) {
						dispatch((SelectionKey) (it.next()));
					}
					selected.clear();
				}
			}
		} catch (IOException ex) {
			ex.printStackTrace();
		}
	}

	void dispatch(SelectionKey k) {
		Runnable r = (Runnable) (k.attachment());
		if (r != null) {
			r.run();
		}
	}

	private class Acceptor implements Runnable {
		private String charset;

		public Acceptor(String charset) {
			this.charset = charset;
		}

		public void run() {
			try {
				SocketChannel socketChannel = serverSocketChannel.accept();
				if (socketChannel != null) {
					new Handler(selector, socketChannel, this.charset);
				}
				logger.info("Connection Accepted by Reactor");
			} catch (IOException ex) {
				logger.error("###### 获取银行连接请求异常", ex);
			}
		}
	}

	private class Handler implements Runnable {
		private final SocketChannel socketChannel;
		private final SelectionKey selectionKey;
		private ByteBuffer receiveByteBuffer = ByteBuffer.allocate(1024);
		private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(1024);
		private String charset;

		Handler(Selector selector, SocketChannel c, String charset) throws IOException {
			this.charset = charset;
			socketChannel = c;
			c.configureBlocking(false);
			selectionKey = socketChannel.register(selector, 0);
			selectionKey.attach(this);
			selectionKey.interestOps(SelectionKey.OP_READ);
			selector.wakeup();
		}

		@Override
		public void run() {

			// ByteBuffer receiveByteBuffer = ByteBuffer.allocate(1024);
			try {
				int readSize = 0;
				while ((readSize = socketChannel.read(receiveByteBuffer)) > 0) {
					// 读写转换
					receiveByteBuffer.flip();
					byteArrayOutputStream.write(receiveByteBuffer.array(), 0, readSize);
				}
				byteArrayOutputStream.flush();

				String input = new String(byteArrayOutputStream.toByteArray(), this.charset);
				logger.info("银行请求数据：{}", input);
			} catch (IOException e) {
				logger.error("###### 接收银行请求数据异常", e);
			}

			try {

				byte[] rsp = "succcesssucccesssucccesssucccesssucccesssucccesssucccesssucccesssucccesssucccesssucccesssucccesssucccesssucccesssucccess22"
						.getBytes(this.charset);
				ByteBuffer buf = ByteBuffer.allocate(rsp.length);
				buf.clear();
				buf.put(rsp);
				buf.flip();
				while (buf.hasRemaining()) {
					socketChannel.write(buf);
				}

				socketChannel.finishConnect();
				socketChannel.close();
			} catch (IOException e) {
				logger.error("###### 返回银行请求数据异常", e);
			}
		}
	}
}
